import re
import random
import types

### Version 0.3
## modulo logico

## Definicion a manopla para formulas de nivel 1
pattern1 = "\S*\(\S*\+\S*\)\S*"      # ( ... + ... ) al menos un mas en el medio
pattern2 = "(\+\)|\*\)|\(\+|\(\*)"   # ni (+ ni (* ni +) ni *)
pattern3 = "^\(\S*\)$"               # parentesis al principio y parentesis al final

CANT_NUM = 6
def imprimirConfiguracion():
    """Usada para debug, muestra informacion del modulo"""
    print "CONSTANTES"
    print "CANT_NUM ", CANT_NUM
    print "OPERANDOS", CANT_NUM - 1

       
#Tal vez deba recibir un parametro NIVEL y en funcion de eso validar o no el string.    
def validarString(s, original):
    """Si el string s es valido segun las convenciones lo devuelve, sino da none

    Returns string"""

    if re.search(pattern2, s):
        return None
    elif re.search(pattern3, s):
        return None
    elif eval(str(s)) == eval(str(original)):  ## Python me obliga a poner str :(
        return None
    elif re.search(pattern1, s):
        return s
    return None # en caso contrario devuelve nada


##Funcion que testea las validaciones para todo el conjunto generado a mano de parentesis posibles
## en un conjunto de 6 numeros
def testearValidarStringNivelUno():
    print "COMIENZA TEST DE VALIDACION DE LECTOR DE STRING..."
    base = "1*2+4*4+5*6"
    str1 = "(1*2+3)*4+5*6"
    str2 = "(1*2+3*4)+5*6"
    str3 = "(1*2+3*4+5)*6"
    str4 = "1*(2+3)*4+5*6"
    str5 = "1*(2+3*4)+5*6"
    str6 = "1*(2+3*4+5)*6"
    str7 = "1*(2+3*4+5*6)"
    str8 = "1*2+(3*4+5)*6"
    str9 = "1*2+(3*4+5*6)"
    str10= "1*2+3*(4+5)*6"
    str11= "1*2+3*(4+5*6)"
    listaStr = [str1, str2, str3, str4, str5, str6, str7, str8, str9, str10, str11]
    errores = [validarString(s, base) for s in listaStr]
    if errores.count(None):
        print "Hay errores de validacion!, verificar str1 .. str11"
    else:
        print "     TEST OK"
    print "FIN DEL TEST!"
    
#notar los parametros opcionales            ---remover---
def generarListaNum(cant_num = CANT_NUM, rango_max = 9):
    """genera  una lista de tamano cant_num, con numeros entre 2 y rango_max
    uqe no pueden estar repetidos"""
    if cant_num + 1 > rango_max:
        return None ### deberia de tirar una excepcion esto es MAL ESTILO TO DO
    lista = []
    while len(lista) < cant_num:
        num = random.randint(2,rango_max)
        if not lista.count(num):
            lista.append(num)
    return lista

def generarListaOp(cant_op = CANT_NUM - 1):
    """ genera una lista de largo cant_op alternando * y +, empezando por *"""
    i = 1
    lista = []
    while i <= cant_op:
        if i % 2:
            lista.append('*')
        else:
            lista.append('+')
        i += 1
    return lista

def alternarElementosLista(lst1, lst2):
    """ alterna los elementos de las listas, generando una lista nueva
        PRE: len(lst1) >= len(lst2)
    Retorna la lista con los elementos alternados"""
    lista = []
    largo = len(lst2)
    i = 0
    while i < largo:
        lista.append(lst1[i])
        lista.append(lst2[i])
        i += 1
    #si quedan elementos en la lista 1 los agrego
    largoNuevo = len(lst1)
    while i < largoNuevo:
        lista.append(lst1[i])
        i += 1
    return lista


### TO DO : Discriminar segun niveles
def generarCasos(cant_num = CANT_NUM, nivel = 1): ## TO DO!
    """Recibe un numero y devuelve una lista con todos los casos posibles y con
    los parentesis bien puestos"""
    listaNum = generarListaNum()
    listaOp   = generarListaOp()
    base = alternarElementosLista(listaNum, listaOp)
    baseStr = listaToString(base)
    i1 = buscarProxNum(base, 0)
    res = []
    while i1 < len(base) : ## mientras tenga sentido poner un '('
        lst = base[:]
        lst.insert(i1, '(')
        i2 = i1 + 6 # lo pongo en la posicion para insertar ')'
        lst2 = lst[:]
        lst2.insert(i2, ')')
        
        aux = listaToString(lst2)
        if validarString(aux, baseStr) != None:
            res.append(aux)
            
        
        while i2 < len(base): ## chequear si es menor o menor e igual TO DO
            lst2 = lst[:]
            i2 += 2
            lst2.insert(i2, ')')
            aux = listaToString(lst2)
            if validarString(aux, baseStr) != None:
                res.append(aux)

        i1 = buscarProxNum(base, i1 + 1)
        
        ## ( 1 * 2 + 3 * 4 + 5 * 6)
    return res

### Ejemplo de devolucion lista = ["(1*2+3)*4 + 5 * 6", "1*2+3 * (4+5)*6"]

def listaToString(lst):
    """Transforma una lista de cualquier cosa en un string conformado por la concatenacion de str de cada elemento """
    texto = ''
    for x in lst:
        texto += str(x)
    return texto

def intToChar(o):
    """Agarra un elemento, y si es un entero lo convierte a string, sino lo deja como esta"""
    if types(o) == types.IntType:
        return str(o)
    

def buscarProxNum(lista, start = 0):
    """Devuelve el proximo indice en la lista donde hay un numero
        PRE: len(lista) > start
    """
    
    lst = lista[start:]
    i = start
    while len(lst) > 0 and type(lst.pop(0)) != types.IntType:
        i += 1
    return i

    
### Pongo parentesis en el primer lugar que pueda --busco siguiente lugar con "numero a la derecha"
    ### Busco indice para el segundo lugar --recorro lista 
            ### Si encuentro pongo y mando a testear/agregar
            ### Si no encuentro indice, avanzo el primer indice
    ###

### [8, '*', 2, '+', 6, '*', 7, '+', 9, '*', 4]


listaNum = generarListaNum()
listaOp   = generarListaOp()
res = alternarElementosLista(listaNum, listaOp)
    
if __name__ == "__main__":
    testearValidarStringNivelUno()
    lst = generarCasos()
    for x in lst:
        print x

